using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace StupidSpaceThing
{
	[Flags]
	public enum FactionColor { None = 1, Red = 2, Green = 4, Blue = 8, Yellow = 16 }

	internal abstract class Faction
	{
		public static List<Faction> Factions = new List<Faction>();
		public static Faction None;
		public static Faction Red;
		public static Faction Green;
		public static Faction Blue;
		public static Faction Yellow;
		public static Faction RandomFaction() {
			switch( Game1.Random.Next(0, 4) ) {
				case 0: return Red;
				case 1: return Green;
				case 2: return Blue;
				case 3: return Yellow;
				default: return null;
			}
		}

		protected Hive hive;
		public Hive Hive { get { return hive; } }
		protected LinkedList<Structure> structures = new LinkedList<Structure>();
		protected LinkedList<Unit> units = new LinkedList<Unit>();
		protected LinkedList<FactionEntity> factionEntities = new LinkedList<FactionEntity>();
		protected LinkedList<DataTrain> dataTrains = new LinkedList<DataTrain>();
		protected Queue<FactionEntity> addNext = new Queue<FactionEntity>();
		public readonly FactionColor FactionColor;
		public readonly Color Color;
		public int Credits = 10000;
		private bool defeated = false;
		public bool Defeated { get { return defeated; } }
		public FactionColor Enemies;

		public LinkedList<Unit> Units { get { return units; } }
		public LinkedList<Structure> Structures { get { return structures; } }
		public LinkedList<FactionEntity> FactionEntities { get { return factionEntities; } }
		public LinkedList<DataTrain> DataTrains { get { return dataTrains; } }		

		public static void Clear() {
			Factions = new List<Faction>();	
			None = new None();
		}

		protected Faction() {			
			this.FactionColor = FactionColor.None;
			this.Color = Color.Gray;
			Factions.Add(this);
		}


		protected Faction(FactionColor color, Vector3 hivePosition) {
			if( color == FactionColor.None ) {
				throw new ApplicationException("The faction with no affiliation is automagically created");
			}
			this.FactionColor = color;
			Factions.Add(this);
			switch( color ) {
				case FactionColor.Red: 
					Red = this; 
					this.Color = Color.Red;
					Enemies = FactionColor.None | FactionColor.Blue | FactionColor.Green | FactionColor.Yellow;
					break;
				case FactionColor.Green: 
					Green = this; 
					this.Color = Color.Green;
					Enemies = FactionColor.None | FactionColor.Blue | FactionColor.Red | FactionColor.Yellow;
					break;
				case FactionColor.Blue: 
					Blue = this; 
					this.Color = Color.SteelBlue;
					Enemies = FactionColor.None | FactionColor.Red | FactionColor.Green | FactionColor.Yellow;
					break;
				case FactionColor.Yellow: 
					Yellow = this; 
					this.Color = Color.Yellow;
					Enemies = FactionColor.None | FactionColor.Blue | FactionColor.Green | FactionColor.Red;
					break;
			}
			hive = new Hive(hivePosition);
			Add(hive);
		}
				
		public static void AddEntity(FactionEntity factionEntity, FactionColor color) {
			foreach( Faction faction in Factions ) {
				if( faction.FactionColor == color ) {
					faction.Add(factionEntity);					
				}
			}
		}

		public void AddNext(FactionEntity factionEntity) {
			factionEntity.Faction = this;
			addNext.Enqueue(factionEntity);
		}

		public void Add(FactionEntity factionEntity) {
			factionEntity.Faction = this;
			if( factionEntity is DataTrain ) {
				dataTrains.AddLast(factionEntity as DataTrain);
			} else {
				factionEntities.AddLast(factionEntity);
				if( factionEntity is Structure ) {
					structures.AddLast(factionEntity as Structure);
					if( factionEntity.Built ) {
						Game1.CurMap.Occupy(factionEntity, false);
					}
				} else if( factionEntity is Unit ) {
					units.AddLast(factionEntity as Unit);
				}
			}
		}

		public void Remove(FactionEntity factionEntity) {			
			if( factionEntity is DataTrain ) {
				dataTrains.Remove(factionEntity as DataTrain);
			} else {
				factionEntities.Remove(factionEntity);
				if( factionEntity is Structure ) {
					structures.Remove(factionEntity as Structure);
					Game1.CurMap.Occupy(factionEntity, true);
					if( factionEntity is Hive ) {
						factionEntity.Faction.Defeat();
					}
				} else if( factionEntity is Unit ) {
					units.Remove(factionEntity as Unit);
				}
			}
		}

		public static void Update(RealGameTime gameTime) {			
			// update
			foreach( Faction faction in Factions ) {
				while( faction.addNext.Count > 0 ) {
					faction.Add(faction.addNext.Dequeue());
				}
				LinkedListNode<FactionEntity> fe = faction.FactionEntities.First;
				while( fe != null ) {
					LinkedListNode<FactionEntity> next = fe.Next;
					if( fe.Value.Dead ) {
						faction.Remove(fe.Value);
					} else if( faction.Defeated ) {
						if( fe.Value is Player ) {
							(fe.Value as Player).ChangeFaction(Faction.None, gameTime);
						} else {
							fe.Value.Entity.Die();
						}
					}
					fe = next;
				}
				if( faction.Defeated ) {
					foreach( DataTrain dt in faction.DataTrains ) {
						dt.Entity.Die();
					}
				} else if( faction != Faction.None ) {
					faction.think();
				}
			}
		}

		public abstract void DamageReport(FactionEntity target, FactionEntity attacker, float damage);
		public abstract void Dead(FactionEntity target, FactionEntity attacker);

		protected abstract void think();

		public void Defeat() {
			Credits = 0;
			defeated = true;
		}
	}
}
